import config from '../../../../config/config.js'

// components
import radio from '../table-column/radio/index.vue'
import checkbox from '../table-column/checkbox/index.vue'
import tooltip from '../../../notice/tooltip/index.vue'
// tools
import { getCell, getColumnByCell, getRowIdentity } from './../unit/util.js'
import debounce from 'throttle-debounce/debounce'
import LayoutObserver from './../unit/layout-observer.js'
// module
export default {
  name: config.prefix + 'table-body',
  mixins: [LayoutObserver],
  components: {
    radio,
    checkbox,
    tooltip
  },
  render (h) {
    return (
      <table class="aty-table__body" cellspacing="0" cellpadding="0" border="0">
        <colgroup>
          {
            this._l(this.columns.filter(column => column.isRender), column => <col name={column.id}/>)
          }
        </colgroup>
        <tbody>  {
          this._l(this.data, (row, $index) => {
            return [<tr style={this.rowStyle ? this.getRowStyle(row, $index) : null}
              key={this.table.rowKey ? this.getKeyOfRow(row, $index) : $index}
              on-dblclick={($event) => this.handleDoubleClick($event, row)}
              on-click={($event) => this.handleClick($event, row, $index)}
              on-contextmenu={($event) => this.handleContextMenu($event, row)}
              on-mouseenter={_ => this.handleMouseEnter($index)}
              on-mouseleave={_ => this.handleMouseLeave()}
              class={[this.getRowClass(row, $index)]}>
              {
                this._l(this.columns, (column, cellIndex) => {
                  if (!column.isRender) {
                    return ''
                  }
                  if (this.mergeCellRecords.length > 0) {
                    for (let i = 0; i < this.mergeCellRecords.length; i++) {
                      let mcr = this.mergeCellRecords[i]
                      if (mcr.rowIndex + mcr.rowspan - 1 >= $index) {
                        if ($index === mcr.rowIndex + mcr.rowspan - 1 && cellIndex === mcr.cellIndex + mcr.colspan - 1) {
                          this.mergeCellRecords.splice(i--, 1)
                        }
                        if (cellIndex >= mcr.cellIndex && cellIndex <= mcr.cellIndex + mcr.colspan - 1 ) {
                          return ''
                        }
                      }
                    }
                  }
                  const { rowspan, colspan } = this.getSpan(row, column, $index, cellIndex)
                  if (!rowspan || !colspan) {
                    return ''
                  } else {
                    let columnContent = column.renderCell.call(
                      this._renderProxy,
                      h,
                      {
                        row,
                        column,
                        $index,
                        store: this.store,
                        _self: this.context || this.table.$vnode.context
                      }
                    )
                    if (rowspan === 1 && colspan === 1) {
                      return (
                        <td class={this.getCellClass($index, cellIndex, row, column, columnContent)}
                          title={this.getTitle(column, columnContent)}
                          on-mouseenter={($event) => this.handleCellMouseEnter($event, row)}
                          on-mouseleave={this.handleCellMouseLeave}>
                          {columnContent}
                        </td>
                      )
                    } else {
                      // 记录单元格合并信息
                      this.mergeCellRecords.push({
                        rowspan: rowspan || 1,
                        colspan: cellIndex + (colspan || 1) > this.columns.length ? this.columns.length - cellIndex : (colspan || 1),
                        rowIndex: $index,
                        cellIndex: cellIndex
                      })
                      return (
                        <td class={this.getCellClass($index, cellIndex, row, column, columnContent)}
                          title={this.getTitle(column, columnContent)}
                          rowspan={rowspan}
                          colspan={colspan}
                          on-mouseenter={($event) => this.handleCellMouseEnter($event, row)}
                          on-mouseleave={this.handleCellMouseLeave}>
                          {columnContent}
                        </td>
                      )
                    }
                  }
                })
              }
            </tr>,
              this.store.isRowExpanded(row)
                ?
                this.fixed == 'right' || this.fixed == 'left'
                  ?
                  (<tr class="aty-table__expanded-row_left_right"></tr>)
                  :
                  (<tr class="aty-table__expanded-row">
                    <td colspan={this.columns.length} class="aty-table__expanded-cell">
                      {this.table.renderExpanded ? this.table.renderExpanded(h, {
                        row,
                        $index,
                        store: this.store
                      }) : ''}
                    </td>
                  </tr>)
                : ''
            ]
          }
          )
        }
        </tbody>
      </table>
    )
  },

  props: {
    store: {
      required: true
    },
    stripe: Boolean,
    context: {},
    rowClassName: [String, Function],
    rowStyle: [Object, Function],
    fixed: String,
    highlight: Boolean
  },
  watch: {
    'store.states.hoverRow' (newVal, oldVal) {
      if (!this.store.states.isComplex) return
      const el = this.$el
      if (!el) return
      const tr = el.querySelector('tbody').children
      const rows = [].filter.call(tr, row => Artery.hasClass(row, 'aty-table__row'))
      const oldRow = rows[oldVal]
      const newRow = rows[newVal]
      if (oldRow) {
        Artery.removeClass(oldRow, 'hover-row')
      }
      if (newRow) {
        Artery.addClass(newRow, 'hover-row')
      }
    },
    'store.states.currentRow' (newVal, oldVal) {
      if (!this.highlight) return
      const el = this.$el
      if (!el) return
      const data = this.store.states.data
      const tr = el.querySelector('tbody').children
      const rows = [].filter.call(tr, row => Artery.hasClass(row, 'aty-table__row'))
      const oldRow = rows[data.indexOf(oldVal)]
      const newRow = rows[data.indexOf(newVal)]
      if (oldRow) {
        Artery.removeClass(oldRow, 'current-row')
      } else {
        [].forEach.call(rows, row => Artery.removeClass(row, 'current-row'))
      }
      if (newRow) {
        Artery.addClass(newRow, 'current-row')
      }
    }
  },

  computed: {
    table () {
      return this.$parent
    },

    data () {
      return this.store.states.data
    },

    columnsCount () {
      if (this.fixed === 'left') {
        return this.store.states.fixedColumns.length;
      } else if (this.fixed === 'right') {
        return this.store.states.rightFixedColumns.length;
      }
      return this.store.states.columns.length;
    },

    columns () {
      if (this.fixed === 'left') {
        return this.store.states.fixedColumns;
      } else if (this.fixed === 'right') {
        return this.store.states.rightFixedColumns;
      } else {
        return this.store.states.columns;
      }
    },

    existFixedColumn() {
      return this.store.states.fixedColumns.length || this.store.states.rightFixedColumns.length
    }
  },

  data () {
    return {
      tooltipContent: ''
    }
  },

  created () {
    this.mergeCellRecords = []
    this.activateTooltip = debounce(50, tooltip => tooltip.handleShowPopper())
  },

  methods: {
    getKeyOfRow (row, index) {
      const rowKey = this.table.rowKey
      if (rowKey) {
        return getRowIdentity(row, rowKey)
      }
      return index
    },

    getSpan (row, column, rowIndex, columnIndex) {
      let rowspan = 1
      let colspan = 1

      const fn = this.table.spanMethod
      if (typeof fn === 'function') {
        const result = fn({
          row,
          column,
          rowIndex,
          columnIndex
        })

        if (Array.isArray(result)) {
          rowspan = result[0]
          colspan = result[1]
        } else if (typeof result === 'object') {
          rowspan = result.rowspan
          colspan = result.colspan
        }
      }

      return {
        rowspan,
        colspan
      }
    },

    getRowStyle(row, rowIndex) {
      const rowStyle = this.table.rowStyle;
      if (typeof rowStyle === 'function') {
        return rowStyle.call(null, {
          row,
          rowIndex
        });
      }
      return rowStyle;
    },

    getRowClass (row, rowIndex) {
      const classes = ['aty-table__row']

      if (this.fixed === 'left') {
        classes.push('aty-table__row--left-fixed');
      } else if (this.fixed === 'right') {
        classes.push('aty-table__row--right-fixed');
      } else {
        classes.push('aty-table__row--center');
      }

      if (this.stripe && rowIndex % 2 === 1) {
        classes.push('aty-table__row--striped')
      }
      const rowClassName = this.table.rowClassName
      if (typeof rowClassName === 'string') {
        classes.push(rowClassName)
      } else if (typeof rowClassName === 'function') {
        classes.push(rowClassName.call(null, {
          row,
          rowIndex
        }))
      }

      if (this.store.states.expandRows.indexOf(row) > -1) {
        classes.push('expanded')
      }

      // [2020-11-16] By Vinsea 
      // 有的视觉交互需要在勾选当前行以后，高亮整行，这里加上新的class，让使用方自己写对应的样式
      if (this.store.states.selection.indexOf(row) > -1) {
        classes.push('selected')
      }

      return classes.join(' ')
    },

    getCellClass (rowIndex, columnIndex, row, column, columnContent) {
      const classes = ['cell', column.id, column.align, column.className]

      if (column.showOverflowTooltip && !Artery.isOneOf(Artery.type(columnContent), ['object', 'array'])) {
        classes.push('tooltip')
      }

      if (column.type === 'expand') {
        classes.push('aty-table__expand-column')
      }

      const cellClassName = this.table.cellClassName
      if (typeof cellClassName === 'string') {
        classes.push(cellClassName)
      } else if (typeof cellClassName === 'function') {
        classes.push(cellClassName.call(null, {
          rowIndex,
          columnIndex,
          row,
          column
        }))
      }

      return classes.filter(c => c).join(' ')
    },

    getTitle (column, columnContent) {
      if (column.showOverflowTooltip && !Artery.isOneOf(Artery.type(columnContent), ['object', 'array'])) {
        return columnContent
      }
      return ''
    },

    handleCellMouseEnter (event, row) {
      const table = this.table
      const cell = getCell(event)

      if (cell) {
        const column = getColumnByCell(table, cell)
        const hoverState = table.hoverState = { cell, column, row }
        /**
         * #event
         *
         * @name    cell-mouse-enter
         * @param   row 行数据
         * @param   column 列数据
         * @param   cell 列DOM对象
         * @param   event 事件
         * @body
         * @description 当单元格 hover 进入时会触发该事件。
         */
        table.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event)
      }

      // 判断是否text-overflow, 如果是就显示tooltip
      const cellChild = event.target.querySelector('.cell')

      if (Artery.hasClass(cellChild, 'tooltip') && cellChild.scrollWidth > cellChild.offsetWidth && this.$refs.tooltip) {
        const tooltip = this.$refs.tooltip
        // TODO 会引起整个 Table 的重新渲染，需要优化
        this.tooltipContent = cell.textContent || cell.innerText
        tooltip.referenceElm = cell
        tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none')
        tooltip.doDestroy()
        tooltip.setExpectedState(true)
        this.activateTooltip(tooltip)
      }
    },

    handleCellMouseLeave (event) {
      const tooltip = this.$refs.tooltip
      if (tooltip) {
        tooltip.setExpectedState(false)
        tooltip.handleClosePopper()
      }
      const cell = getCell(event)
      if (!cell) return

      const oldHoverState = this.table.hoverState || {}
      /**
       * #event
       *
       * @name    cell-mouse-leave
       * @param   row 行数据
       * @param   column 列数据
       * @param   cell 列DOM对象
       * @param   event 事件
       * @body
       * @description 当单元格 hover 离开时会触发该事件。
       */
      this.table.$emit('cell-mouse-leave', oldHoverState.row, oldHoverState.column, oldHoverState.cell, event)
    },

    handleMouseEnter (index) {
      this.store.commit('setHoverRow', index)
    },

    handleMouseLeave () {
      this.store.commit('setHoverRow', null)
    },

    handleContextMenu (event, row) {
      /**
       * #event
       *
       * @name    row-contextmenu
       * @param   row 行数据
       * @param   event 事件
       * @body
       * @description 当某一行被鼠标右键点击时会触发该事件。
       */
      this.handleEvent(event, row, 'contextmenu')
    },

    handleDoubleClick (event, row) {
      /**
       * #event
       *
       * @name    cell-dblclick
       * @param   row 行数据
       * @param   column 列数据
       * @param   cell 列DOM对象
       * @param   event 事件
       * @body
       * @description 当某个单元格被双击击时会触发该事件。
       */
      /**
       * #event
       *
       * @name    row-dblclick
       * @param   row 行数据
       * @param   event 事件
       * @body
       * @description 当某一行被双击时会触发该事件。
       */
      this.handleEvent(event, row, 'dblclick')
    },

    handleClick (event, row, $index) {
      // 判断当前点击的行 是否可以被选择（1、没有radio列 或者 2、radio列没有selectable方法 或者 3、调用selectable方法来判断当前行是否可以被选择）
      const radioColumns = this.columns.filter(column => column.type === 'radio')
      const radioColumn = radioColumns.length > 0 ? radioColumns[0] : null
      let canSelectedRow = !radioColumn || !(typeof radioColumn.selectable === 'function') || radioColumn.selectable.call(null, row, $index)
      // 如果可以被选择，调用store中的setCurrentRow方法，去提交current-change事件
      if (canSelectedRow) {
        this.store.commit('setCurrentRow', row)
      }

      /**
       * #event
       *
       * @name    cell-click
       * @param   row 行数据
       * @param   column 列数据
       * @param   cell 列DOM对象
       * @param   event 事件
       * @body
       * @description 当某一行被点击时会触发该事件。
       */
      /**
       * #event
       *
       * @name    row-click
       * @param   row 行数据
       * @param   event 事件
       * @body
       * @description 当某一行被点击时会触发该事件。
       */
      this.handleEvent(event, row, 'click')
    },

    handleEvent (event, row, name) {
      const table = this.table
      const cell = getCell(event)
      let column
      if (cell) {
        column = getColumnByCell(table, cell)
        if (column) {
          table.$emit(`cell-${name}`, row, column, cell, event)
        }
      }
      table.$emit(`row-${name}`, row, event, column)
    },

    handleExpandClick (row, e) {
      e.stopPropagation()
      this.store.toggleRowExpansion(row)
      if (this.store.isRowExpanded(row) && this.existFixedColumn) {
        this.$nextTick(() => {
          const expandedTrs = this.table.$el.querySelectorAll('.aty-table__expanded-row');
          const fixedExpandedTrs = this.table.$el.querySelectorAll('.aty-table__expanded-row_left_right');
          fixedExpandedTrs.forEach((tr, i) => {
            tr.style.height = expandedTrs[i % expandedTrs.length].offsetHeight + 'px';
          })
        })
      }
    }
  }
}
